 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>单链表实现贪吃蛇</title>
</head>
<style>
     .snakeDome{
          width:100vw;
          height: 100vh;
          position: relative;
     }
     .snakeDome > div{
           width: 40px;
           height: 40px;
           background-color: red;
           position: absolute;
           border-radius: 50%;
     }
     .food{
          background-color: green !important;
          border-radius: 50%;
     }
</style>
<body>
    <div>
        <div class="snakeDome"></div>
    </div>
    <script>
         let config = {
             size:40,   // 一单位身体的 宽 高
             gamecontent:document.getElementsByClassName('snakeDome')[0]  // 容器
        }
           function linklist(x,y){
             this.next = null; 
             this.dome = document.createElement('div'); 
             config.gamecontent.appendChild(this.dome);
             this.x = x;
             this.y = y;
             this.init();
        }
        linklist.prototype.setpos = function(x,y){
                  this.x = x;
                  this.y = y;
                  this.init();
        }
        linklist.prototype.movebody = function(){
             //不断递归下一个的新位置给自身赋值 直到next为null 
        	   //提醒一下这里的this 执行必要搞明白了，要不会搞的一塌糊涂
             if(this.next){
                  this.setpos(this.next.x,this.next.y);
                  this.next.movebody();
             }
        }
        snakeinit.prototype.rendoDome = function(){
             let foods = document.getElementsByClassName('food')[0];
             if(foods){
                 config.gamecontent.removeChild(foods); 
             }
             this.rX = parseInt(Math.random()*10+4);  // 随机X坐标
             this.rY = parseInt(Math.random()*10+6);  // 随机Y坐标
             let domes = document.createElement('div');
             config.gamecontent.appendChild(domes);
             domes.className='food';
             domes.style['top'] = this.rY*40+'px';
             domes.style['left'] = this.rX*40+'px';
            
        }
        snakeinit.prototype.getnextpos = function(){
              this.posY++
                  return {
                      x:this.posX,
                      y:10  
                };
        }
        linklist.prototype.setnextnode = function(nextnode){
             this.next = nextnode;
             
        }
        linklist.prototype.init = function(){
                    
                    this.dome.style['left'] = this.x*config.size+'px';
                    this.dome.style['top'] = this.y*config.size+'px';
                    
                }
                function snakeinit(len){
                    this.direction = 'right';   // 默认方向
                    this.pos=len-1;             // 长度用于计算移动
                    this.posX=this.pos;         // 横向坐标
                    this.posY=this.pos;         // 纵向坐标
                    this.curItem = null;        // 蛇头（注：使用单链表只需要控制蛇头方向和行动距离即可，身体利用链表特性移动）  
                    this.rX = 0;
                    this.rY = 0;
                    for(let n = 0;n<len;n++){
                            let item =  new linklist(n,this.pos);
                            if(n==0){
                                console.log(item)
                            this.hand=item; //蛇尾 遍历的第一个为蛇尾
                            }
                            if (this.curItem !== null){
                                this.curItem.setnextnode(item);
                            }
                            this.curItem = item;// 蛇头 遍历最后一个为蛇头
                        }
                    this.rendoDome();    
                }
 //老样子 改造咱们之前的代码
 snakeinit.prototype.movenext = function(){
                let pos = this.getnextpos();
            //  先移动身体 再设置头部位置
            //  思路从头部设置值（也就是最后一项）
                 this.hand.movebody();
            //  身体根据头部（curItem）移动距离 控制着整个身体
                 this.curItem.setpos(pos.x,pos.y);
                 this.insertBody(); //每次都会执行此函数
        }
  //再配合咱们automove的定时器 就让他自己动起来了
  snakeinit.prototype.automove = function(){
             setInterval(() => {
                  this.movenext()
             }, 500);
        }
 
  //简单改造一下咱们的getnextpos函数
     snakeinit.prototype.getnextpos = function(){
              switch(this.direction){
                  case 'up':
                  this.posY--;break;
                  case 'down':
                  this.posY++;break;
                  case 'right':
                  this.posX++;break;
                  default:
                  this.posX--;
                 }
                  return {
                      x:this.posX,
                      y:this.posY
                };
        }
        snakeinit.prototype.insertBody = function(){
                 // 吃掉事物之后 获取下一个位置坐标 并让新的DOME充当蛇头再重新设置食物的位置
                 // 每次判断头部和食物的位置相同直接就将食物吃掉 换新的头部就可以啦
                 if(this.posX==this.rX && this.posY==this.rY){
                      let pos = this.getnextpos();
                      let newitem =  new linklist(pos.x,pos.y);
                      this.curItem.next = newitem;
                      this.curItem = newitem;
                      this.rendoDome();
                 }
              
        }
        snakeinit.prototype.changeMove = function(type){
             this.direction = type;
        }
        let num = 9; // 初始化长度自定义即可
        let s = new snakeinit(num);
        s.automove()
        window.onkeydown = function(e){
            console.log(e)
            if(e.key=='ArrowUp'){
                 s.changeMove('up');
            }else if(e.key=='ArrowDown'){
                 s.changeMove('down');
            }else if(e.key=='ArrowLeft'){
                 s.changeMove('left');
            }else if(e.key=='ArrowRight'){
                 s.changeMove('right');
            }
      }
    </script>
</body>
</html>